import React, { useEffect, useState, useRef } from 'react';
import styles from './index.less';
import './index.less';
import G6 from '@antv/g6';
import { rawData, cavasConfig } from './data';

const Index = () => {
  const { Util } = G6;
  // 定义画布
  let canvasTree: any;
  // 选择节点Id
  const activeRef: any = useRef(null);
  // 选中节点收缩状态
  const collapsedRef: any = useRef(null);
  // 画布树形节点数据
  let rawDataArr: any = [rawData];
  // 用于接收选中节点信息
  const evtArrIsRef: any = useRef(null);
  // 弹窗显示状态
  const [isModalOpen, setIsModalOpen] = useState<boolean>(false);

  // 初始化
  useEffect(() => {
    // 获取容器元素并创建一个包含说明文字的 <pre> 元素
    const container = document.getElementById('container') as HTMLElement;
    // 设置画布的宽度和高度
    const width = container.scrollWidth;

    // 创建一个 G6 树图实例，并配置参数
    canvasTree = new G6.TreeGraph({
      ...cavasConfig,
      width, // 设置宽度
      layout: {
        // 使用 mindmap 布局
        type: 'mindmap',
        // 横向布局
        direction: 'H',
        // 设置节点高度
        getHeight: () => {
          return 16;
        },
        // 设置节点宽度
        getWidth: (node) => {
          return node.level === 0
            ? Util.getTextSize(node.label, 16)[0] + 12 // 根节点字体较大
            : Util.getTextSize(node.label, 12)[0]; // 子节点字体较小
        },
        // 设置垂直间隔
        getVGap: () => {
          return 30;
        },
        // 设置水平间隔
        getHGap: () => {
          return 100;
        },
        // 获取节点方向
        getSide: (node) => {
          return node.data.direction;
        },
      },
    });

    disableDefaultContextMenu(container);
    contextmenuFn();
    isMouseOutsideElement();

    // 加载数据并渲染树图
    canvasTree.data(dataTransform(rawData));

    // 渲染树图
    canvasTree.render();
  }, []);

  // 注册根节点类型
  G6.registerNode(
    'dice-mind-map-root',
    {
      jsx: (cfg) => {
        const width = Util.getTextSize(cfg.label, 16)[0] + 24;
        const stroke = cfg.style.stroke || '#096dd9';
        const fill = cfg.style.fill;

        return `
		  <group>
			<rect draggable="true" style={{width: ${width}, height: 42, stroke: ${stroke}, radius: 4}} keyshape>
			  <text style={{ fontSize: 16, marginLeft: 12, marginTop: 12 }}>${
          cfg.label
        }</text>
			  <text style={{ marginLeft: ${
          width - 16
        }, marginTop: -20, stroke: '#66ccff', fill: '#000', cursor: 'pointer', opacity: ${
          cfg.hover ? 0.75 : 0
        } }} action="add">+</text>
			</rect>
		  </group>
		`;
      },
      getAnchorPoints() {
        return [
          [0, 0.5],
          [1, 0.5],
        ];
      },
    },
    'single-node',
  );

  // 注册子节点---第二层节点
  G6.registerNode(
    'dice-mind-map-sub',
    {
      jsx: (cfg: any) => {
        // 计算节点宽度
        const width = Util.getTextSize(cfg.label, 14)[0] + 24;
        const color = cfg.color || cfg.style.stroke;

        return `
            <group>
              <rect draggable="true" style={{width: ${
                width + 24
              }, height: 22}} keyshape>
                <text draggable="true" style={{ fontSize: 14, marginLeft: 12, marginTop: 6 }}>${
                  cfg.label
                }</text>
                <text style={{ marginLeft: ${
                  width - 8
                }, marginTop: -10, stroke: ${color}, fill: '#000', cursor: 'pointer', opacity: ${
          cfg.hover ? 0.75 : 0
        }, next: 'inline' }} action="add">+</text>
                <text style={{ marginLeft: ${
                  width - 4
                }, marginTop: -10, stroke: ${color}, fill: '#000', cursor: 'pointer', opacity: ${
          cfg.hover ? 0.75 : 0
        }, next: 'inline' }} action="delete">-</text>
              </rect>
              <rect style={{ fill: ${color}, width: ${
          width + 24
        }, height: 2, x: 0, y: 22 }} />
              
            </group>
          `;
      },
      getAnchorPoints() {
        return [
          [0, 0.965],
          [1, 0.965],
        ];
      },
    },
    'single-node',
  );
  // 注册自定义叶子节点类型 ---- 第三层节点
  G6.registerNode(
    'dice-mind-map-leaf',
    {
      // 定义节点的 JSX 结构
      jsx: (cfg: any) => {
        // 计算节点宽度
        const width = Util.getTextSize(cfg.label, 12)[0] + 24;
        // 获取节点颜色
        const color = cfg.color || cfg.style.stroke;
        // 返回节点的 JSX 结构
        return `
            <group>
              <rect draggable="true" style={{width: ${
                width + 20
              }, height: 26, fill: 'transparent' }}>
                <text style={{ fontSize: 12, marginLeft: 12, marginTop: 6 }}>${
                  cfg.label
                }</text>
                    <text style={{ marginLeft: ${
                      width - 8
                    }, marginTop: -10, stroke: ${color}, fill: '#000', cursor: 'pointer', opacity: ${
          cfg.hover ? 0.75 : 0
        }, next: 'inline' }} action="add">+</text>
                    <text style={{ marginLeft: ${
                      width - 4
                    }, marginTop: -10, stroke: ${color}, fill: '#000', cursor: 'pointer', opacity: ${
          cfg.hover ? 0.75 : 0
        }, next: 'inline' }} action="delete">-</text>
              </rect>
              <rect style={{ fill: ${color}, width: ${
          width + 24
        }, height: 2, x: 0, y: 32 }} />
              
            </group>
          `;
      },
      // 定义锚点位置
      getAnchorPoints() {
        return [
          [0, 0.965], // 左侧锚点
          [1, 0.965], // 右侧锚点
        ];
      },
    },
    'single-node',
  );

  /**
   * 注册一个行为 'scroll-canvas'，用于处理画布的滚动事件。
   * 当用户滚动鼠标滚轮时，根据情况调整画布的缩放或平移。
   * 如果按下 Ctrl 键并滚动鼠标滚轮，则缩放画布。
   * 否则，根据鼠标滚轮的移动距离来平移画布。
   * @returns {object} 返回包含滚动事件处理函数的对象。
   */
  G6.registerBehavior('scroll-canvas', {
    // 定义获取事件的方法，返回一个包含 wheel 事件处理函数的对象
    getEvents: function getEvents() {
      return {
        wheel: 'onWheel', // 当发生滚轮事件时，调用 onWheel 方法处理
      };
    },
    // 处理滚轮事件的方法
    onWheel: function onWheel(ev) {
      const { graph } = this; // 获取当前行为关联的图形实例
      if (!graph) {
        return; // 如果图形实例不存在，则直接返回
      }

      if (ev.ctrlKey) {
        // 如果同时按下了 Ctrl 键
        const canvas = graph.get('canvas'); // 获取画布实例
        const point = canvas.getPointByClient(ev.clientX, ev.clientY); // 根据鼠标位置获取画布上的点坐标

        let ratio = graph.getZoom(); // 获取当前缩放比例
        if (ev.wheelDelta > 0) {
          // 如果滚轮向上滚动
          ratio += ratio * 0.05; // 增加缩放比例
        } else {
          // 如果滚轮向下滚动
          ratio *= ratio * 0.05; // 减小缩放比例
        }

        // 缩放图形到指定的缩放比例，并以指定点为中心进行缩放
        graph.zoomTo(ratio, {
          x: point.x,
          y: point.y,
        });
      } else {
        // 如果没有按下 Ctrl 键
        const x = ev.deltaX || ev.movementX; // 获取水平方向的滚动距离
        const y = ev.deltaY || ev.movementY || (-ev.wheelDelta * 125) / 3; // 获取垂直方向的滚动距离

        // 平移图形的视口位置
        graph.translate(-x, -y);
      }

      ev.preventDefault(); // 阻止默认滚动行为
    },
  });

  /**
   * 函数转换数据
   * @param data 节点数据
   * @returns
   */
  const dataTransform = (data: []) => {
    // 定义递归函数 changeData，用于转换数据结构和添加属性
    const changeData = (d, level = 0, color) => {
      // 创建一个新的数据对象，保留原始数据不变
      const data = {
        ...d,
      };
      // 根据层级 level 设置节点类型 type
      switch (level) {
        case 0:
          data.type = 'dice-mind-map-root'; // 根节点类型为 'dice-mind-map-root'
          break;
        case 1:
          data.type = 'dice-mind-map-sub'; // 子节点类型为 'dice-mind-map-sub'
          break;
        default:
          data.type = 'dice-mind-map-leaf'; // 其他情况为叶子节点类型 'dice-mind-map-leaf'
          break;
      }

      data.hover = false; // 初始化 hover 属性为 false

      // 如果指定了颜色 color，则设置节点颜色为该颜色
      if (color) {
        data.color = color;
      }

      // 当层级为 1 且未指定方向时，根据节点 id 的最后一个字符决定方向
      if (level === 1 && !d.direction) {
        if (!d.direction) {
          data.direction =
            d.id.charCodeAt(d.id.length - 1) % 2 === 0 ? 'right' : 'left'; // 根据 id 最后一个字符的 ASCII 码决定方向
        }
      }

      // 如果当前节点有子节点，则递归处理每个子节点
      if (d.children) {
        data.children = d.children.map((child) =>
          changeData(child, level + 1, data.color),
        ); // 递归处理子节点数据
      }
      return data; // 返回处理后的数据对象
    };

    return changeData(data); // 调用递归函数，返回整体转换后的数据
  };

  /**
   * 鼠标右键--需要显示选项卡，根据坐标系显示位置
   * @param x
   * @param y
   */
  function showModal(x: number, y: number) {
    const modal: any = document.getElementById('my-modal');
    modal.style.display = 'block';
    modal.style.left = x + 'px';
    modal.style.top = y + 'px';
  }

  // 隐藏模态框
  function hideModal() {
    const modal: any = document.getElementById('my-modal');
    modal.style.display = 'none';
  }

  /**
   * 禁用画布的默认右键菜单
   * @param  container 画布元素
   */
  const disableDefaultContextMenu = (container: any) => {
    container.addEventListener('contextmenu', (event: Event) => {
      event.preventDefault();
    });
  };
  /**
   * 节点鼠标右键触发函数
   */
  const contextmenuFn = () => {
    // 监听右键事件
    canvasTree.on('node:contextmenu', (evt: any) => {
      const { canvasX, canvasY } = evt;
      activeRef.current = evt.item._cfg.id;
      evtArrIsRef.current = evt;
      showModal(canvasX, canvasY);
    });
  };
  /**
   * 节点右键选中后的弹窗--鼠标移出去后隐藏
   */
  const isMouseOutsideElement = () => {
    const modelElement = document.getElementById('my-modal') as HTMLElement;

    // 监听画布的鼠标移动事件
    modelElement.addEventListener('mouseleave', (evt) => {
      // 否则隐藏模态框
      hideModal();
    });
  };

  /**
   * 节点展开收缩
   * @param status 1:收缩 2:展开 3: 新增 4:删除
   */
  const isCollapsedExpanded = (status: number) => {
    hideModal();
    const { item, currentTarget } = evtArrIsRef.current;
    if (status === 1) {
      collapsedRef.current = true;
      // 更新画布中某个节点的属性并重新布局图，第二个参数是节点对象
      currentTarget.updateItem(item, {
        collapsed: collapsedRef.current,
      });
    } else if (status === 2) {
      collapsedRef.current = false;
      // 更新画布中某个节点的属性并重新布局图，第二个参数是节点对象
      currentTarget.updateItem(item, {
        collapsed: collapsedRef.current,
      });
    } else if (status === 3) {
      // 获取节点模型数据
      const model = item.get('model');
      // 节点id
      const newId =
        model.id +
        '-' +
        (((model.children || []).reduce((a, b) => {
          const num = Number(b.id.split('-').pop());
          return a < num ? num : a;
        }, 0) || 0) +
          1);
      currentTarget.updateItem(item, {
        children: (model.children || []).concat([
          {
            id: newId,
            direction:
              newId.charCodeAt(newId.length - 1) % 2 === 0 ? 'right' : 'left',
            // 设置新节点标签
            label: '新增节点',
            // 设置新节点类型
            type: 'dice-mind-map-leaf',
            color: model.color,
          },
        ]),
      });
    } else if (status === 4) {
      // 获取节点模型数据
      const model = item.get('model');
      // 获取父节点
      const parent = item.get('parent');
      currentTarget.updateItem(parent, {
        children: (parent.get('model').children || []).filter(
          (e) => e.id !== model.id,
        ),
      });
    } else if (status === 5) {
      isModalOpenFn();
    }
    // 局部更新画布
    evtArrIsRef.current.currentTarget.layout(false);
  };

  /**
   * 模态框组件---打开，关闭
   */
  const isModalOpenFn = () => {
    setIsModalOpen(!isModalOpen);
  };

  return (
    <div className={styles.box}>
      <div id="container" />
      <div id="my-modal" className={styles.customPopup}>
        <div
          onClick={() => {
            isCollapsedExpanded(1);
          }}
          className={styles.customPopupText}
        >
          收缩节点
        </div>
        <div
          onClick={() => {
            isCollapsedExpanded(2);
          }}
          className={styles.customPopupText}
        >
          展开节点
        </div>
        <div
          onClick={() => {
            isCollapsedExpanded(3);
          }}
          className={styles.customPopupText}
        >
          新增节点
        </div>
        <div
          onClick={() => {
            isCollapsedExpanded(4);
          }}
          className={styles.customPopupText}
        >
          删除节点
        </div>
        <div
          onClick={() => {
            isCollapsedExpanded(5);
          }}
          className={styles.customPopupText}
        >
          节点交互
        </div>
      </div>
    </div>
  );
};
export default Index;
